/**
 * 作者：
 * 创建时间：2012-6-28 上午10:10:06
 */
package dragon.core.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dragon.core.exception.ExceptionHandler;

/**
 * 基于java反射的应用
 */
public class Reflect {

	/**
	 * 日志对象
	 */
	private static Logger logger = LoggerFactory.getLogger(Reflect.class);

	/**
	 * 缓存所有类型的字段定义<br>
	 * k:类型，v:字段数组
	 */
	private static Map<Class<?>, Field[]> declaredFields = new HashMap<Class<?>, Field[]>();

	/**
	 * 缓存所有类型的方法定义<br>
	 * k:类型，v:方法数组
	 */
	private static Map<Class<?>, Method[]> declaredMethods = new HashMap<Class<?>, Method[]>();

	/**
	 * 获取类型字段定义(包含父类中定义的字段)
	 * 
	 * @param cls
	 *            类型
	 * @return
	 */
	public static Field[] getDeclaredFields(Class<?> cls) {

		Field[] fields = declaredFields.get(cls);
		if (fields != null) {
			// if (logger.isDebugEnabled()) {
			// logger.debug("已解析过类型字段，直接返回数据：" + cls.getCanonicalName() + "\tfields:" + fields.length);
			// }
			return fields;
		}

		// 得到父类定义的字段
		Field[] parentDeclaredFields = null;
		Class<?> parent = cls.getSuperclass();
		if (parent != null && !parent.getName().equals("java.lang.Object")) {
			parentDeclaredFields = getDeclaredFields(parent);
		}

		// 得到本类定义的字段
		Field[] selfDeclaredFields = cls.getDeclaredFields();

		// TODO:需要处理重写逻辑，即父子类中有同名属性的话，保留子类中的属性
		// 计算最终数据
		Field[] allDeclaredFields = null;
		if (parentDeclaredFields != null && parentDeclaredFields.length > 0) {
			// 合并字段
			int parentDeclaredFieldslength = parentDeclaredFields.length;
			int selfDeclaredFieldslength = selfDeclaredFields.length;
			allDeclaredFields = new Field[parentDeclaredFieldslength + selfDeclaredFieldslength];
			// 加入父类定义的字段
			for (int i = 0; i < parentDeclaredFieldslength; i++) {
				allDeclaredFields[i] = parentDeclaredFields[i];
			}
			// 加入本类定义的字段
			for (int i = 0; i < selfDeclaredFieldslength; i++) {
				allDeclaredFields[i + parentDeclaredFieldslength] = selfDeclaredFields[i];
			}
		} else {
			allDeclaredFields = selfDeclaredFields;
		}

		declaredFields.put(cls, allDeclaredFields);
		return allDeclaredFields;

	}

	/**
	 * 返回指定类型的指定字段
	 * 
	 * @param cls
	 *            类型
	 * @param fieldName
	 *            字段名
	 * @return
	 */
	public static Field getDeclaredField(Class<?> cls, String fieldName) {
		Field[] fields = getDeclaredFields(cls);
		for (Field field : fields) {
			// TODO:优化查找性能
			if (field.getName().equalsIgnoreCase(fieldName)) {
				// java本身字段是区分大小写的，由于业务的特殊性(类中不会同时存在xxx和XXX的定义)，比较时不区分大小写
				return field;
			}
		}
		return null;
	}

	/**
	 * 获取类型方法定义(包含父类中定义的方法)
	 * 
	 * @param cls
	 *            类型
	 * @return
	 */
	public static Method[] getDeclaredMethods(Class<?> cls) {
		Method[] methods = declaredMethods.get(cls);
		if (methods != null) {
			// if (logger.isDebugEnabled()) {
			// logger.debug("已解析过类型方法，直接返回数据：" + cls.getCanonicalName() + "\tmethods:" + methods.length);
			// }
			return methods;
		}

		// 得到父类定义的方法
		Method[] parentDeclaredMethods = null;
		Class<?> parent = cls.getSuperclass();
		if (parent != null && !parent.getName().equals("java.lang.Object")) {
			parentDeclaredMethods = getDeclaredMethods(parent);
		}

		// 得到本类定义的方法
		Method[] selfDeclaredMethods = cls.getDeclaredMethods();

		// TODO:需要处理重写逻辑，即父子类中有相同方法的话，保留子类中的方法
		// 计算最终数据
		Method[] allDeclaredMethods = null;
		if (parentDeclaredMethods != null && parentDeclaredMethods.length > 0) {
			// 合并方法
			int parentDeclaredMethodslength = parentDeclaredMethods.length;
			int selfDeclaredMethodslength = selfDeclaredMethods.length;
			allDeclaredMethods = new Method[parentDeclaredMethodslength + selfDeclaredMethodslength];
			// 加入父类定义的字段
			for (int i = 0; i < parentDeclaredMethodslength; i++) {
				allDeclaredMethods[i] = parentDeclaredMethods[i];
			}
			// 加入本类定义的字段
			for (int i = 0; i < selfDeclaredMethodslength; i++) {
				allDeclaredMethods[i + parentDeclaredMethodslength] = selfDeclaredMethods[i];
			}
		} else {
			allDeclaredMethods = selfDeclaredMethods;
		}

		declaredMethods.put(cls, allDeclaredMethods);
		return allDeclaredMethods;
	}

	/**
	 * 获取类型的指定方法，从以缓存的类型方法中进行筛选
	 * 
	 * @param cls
	 *            类型
	 * @param methodName
	 *            方法名
	 * @param parameterTypes
	 *            参数类型
	 * @return
	 */
	public static Method getDeclaredMethod(Class<?> cls, String methodName, Class<?>... parameterTypes) {
		Method[] methods = getDeclaredMethods(cls);
		// TODO:优化查找性能
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				boolean equal = true;
				Class<?>[] pts = method.getParameterTypes();
				if (pts.length == parameterTypes.length) {
					for (int i = pts.length - 1; i >= 0; i--) {
						if (pts[i] != parameterTypes[i]) {
							equal = false;
						}
					}
				} else {
					equal = false;
				}
				if (equal) {
					return method;
				}
			}
		}
		return null;
	}

	/**
	 * 获取 getter 方法
	 * 
	 * @param cls
	 *            类型
	 * @param field
	 *            字段
	 * @return
	 */
	public static Method getGetterMethod(Class<?> cls, Field field) {
		String fieldName = field.getName();
		String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		Method getMethod = getDeclaredMethod(cls, getMethodName);

		// boolean 类型的字段 get 方法名比较特殊，一般是 isXXX，例如：
		// 属性 get方法名
		// ok isOk
		// isok isIsok，这种比较特殊，is后面首字符不是大写的话，会多加一个is
		// isOk isOk，这种比较特殊，is后面首字符大写，则get方法名和属性名一致
		if (getMethod == null) {
			if (field.getType().getName().equals("boolean")) {
				if (!fieldName.startsWith("is") || (fieldName.startsWith("is") && (fieldName.charAt(2) < 65 || fieldName.charAt(2) > 90))) {
					getMethodName = "is" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
				} else {
					getMethodName = fieldName;
				}
				getMethod = getDeclaredMethod(cls, getMethodName);
			}
		}

		return getMethod;
	}

	/**
	 * 获取 setter 方法
	 * 
	 * @param cls
	 *            类型
	 * @param field
	 *            字段
	 * @return
	 */
	public static Method getSetterMethod(Class<?> cls, Field field) {
		String fieldName = field.getName();
		String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		return getDeclaredMethod(cls, setMethodName, field.getType());
	}

	/**
	 * 实现同一类型的两个对象间数据拷贝<br>
	 * 将 from 中存在而 to 中不存在的属性值拷贝到 to 中
	 * 
	 * @param from
	 *            源对象
	 * @param to
	 *            目标对象
	 */
	public static void copy(Object from, Object to) {

		// 空对象返回
		if (from == null || to == null) {
			return;
		}

		// 类型不一致，直接返回
		try {
			from.getClass().cast(to);
		} catch (ClassCastException e) {
			return;
		}

		// 对象类型
		Class<?> cls = from.getClass();

		Class<?> fieldType = null;
		Method getMethod = null;
		Method setMethod = null;
		Object value1 = null;
		Object value2 = null;
		Field[] fields = getDeclaredFields(cls);
		for (Field field : fields) {
			fieldType = field.getType();
			// 对于整形字段，如果 from 中值不为 -1 ，而 to 中为 -1 ，则将 from 中的值拷贝到 to 中
			// 对于其它类型的字段，如果 from 中值不为 null ，而 to 中为 null ，则将 from 中的值拷贝到 to 中

			// 取get方法，判断obj2中属性值是否存在
			getMethod = getGetterMethod(cls, field);
			if (getMethod == null) {
				continue;
			}

			try {
				// 取obj2中的字段值
				value2 = getMethod.invoke(to);
				if (fieldType.getCanonicalName().equals("int")) {
					// 对于整形字段
					if ((Integer) value2 == -1) {
						// 取obj1中的字段值
						value1 = getMethod.invoke(from);
						if ((Integer) value1 != -1) {
							// 取set方法
							setMethod = getSetterMethod(cls, field);
							// 将obj1中值拷贝到obj2中
							if (setMethod != null) {
								setMethod.invoke(to, value1);
							}
						}
					}
				} else {
					// 非整形字段
					if (value2 == null) {
						// 取obj1中的字段值
						value1 = getMethod.invoke(from);
						if (value1 != null) {
							// 取set方法
							setMethod = getSetterMethod(cls, field);
							// 将obj1中值拷贝到obj2中
							if (setMethod != null) {
								setMethod.invoke(to, value1);
							}
						}
					}
				}
			} catch (IllegalArgumentException e) {
				logger.error(e.getMessage(), e);
			} catch (IllegalAccessException e) {
				logger.error(e.getMessage(), e);
			} catch (InvocationTargetException e) {
				logger.error(e.getMessage(), e);
			}
		}

	}

	/**
	 * 初始化对象，清除对象中int型的字段默认值并设定为指定值<br>
	 * 目的是为了解决同一类型的两个对象间属性值拷贝时，对于int型字段，不好判断属性值是默认值还是自定义的值
	 * 
	 * @param obj
	 */
	public static void initObject(Object obj) {

		if (obj == null) {
			return;
		}

		// 对象类型
		Class<?> cls = obj.getClass();

		// 类中定义的字段
		Field[] fields = getDeclaredFields(cls);

		Class<?> fieldType = null;
		Method setMethod = null;
		for (Field field : fields) {

			// 对于子类中特有而父类中没有的属性，不需要初始化
			try {
				if (cls.getDeclaredField(field.getName()) != null) {
					continue;
				}
			} catch (SecurityException e) {
				logger.error(e.getMessage(), e);
			} catch (NoSuchFieldException e) {
			}

			fieldType = field.getType();
			if (fieldType.getCanonicalName().equals("int")) {
				setMethod = getSetterMethod(cls, field);
				if (setMethod != null) {
					try {
						setMethod.invoke(obj, -1);
					} catch (IllegalArgumentException e) {
						logger.error(e.getMessage(), e);
					} catch (IllegalAccessException e) {
						logger.error(e.getMessage(), e);
					} catch (InvocationTargetException e) {
						logger.error(e.getMessage(), e);
					}
				}
			}
		}
	}

	/**
	 * 将对象转换为Map形式
	 * 
	 * @param obj
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Map toMap(Object obj) {
		if (obj == null) {
			return null;
		}

		// 如果obj是Map类型，直接返回
		if (obj instanceof Map) {
			return (Map) obj;
		}

		// 继承自Map，直接返回
		try {
			Map.class.cast(obj);
			return (Map) obj;
		} catch (ClassCastException e) {
		}

		// 其它类型，进行转换
		Map<String, Object> map = new HashMap<String, Object>();
		Class cls = obj.getClass();
		Field[] fields = getDeclaredFields(cls);
		String fieldName = null;
		Method getMethod = null;
		for (Field field : fields) {
			fieldName = field.getName();

			getMethod = getGetterMethod(cls, field);
			if (getMethod == null) {
				continue;
			}

			try {
				map.put(fieldName.toUpperCase(), getMethod.invoke(obj));
			} catch (IllegalArgumentException e) {
				logger.error(e.getMessage(), e);
			} catch (IllegalAccessException e) {
				logger.error(e.getMessage(), e);
			} catch (InvocationTargetException e) {
				logger.error(e.getMessage(), e);
			}
		}
		getMethod = null;
		fieldName = null;
		fields = null;
		cls = null;

		return map;
	}

	/**
	 * 将map内容赋值于对象中相应的属性
	 * 
	 * @param map
	 * @param obj
	 *            对象
	 * @throws ExceptionHandler
	 */
	public static void map2Obj(Map<String, Object> map, Object obj) throws ExceptionHandler {
		Class<?> cls = obj.getClass();
		Field[] fields = cls.getDeclaredFields();
		Method setMethod = null;
		Object value = null;
		String fieldName = null;
		String fieldType = null;
		String valueStr = null;
		for (Field field : fields) {
			fieldName = field.getName();
			if (map.containsKey(fieldName.toUpperCase())) {
				// 类对象中存在的属性，赋值
				fieldType = field.getType().getSimpleName();
				valueStr = map.get(fieldName.toUpperCase()) != null ? map.get(fieldName.toUpperCase()).toString() : "";

				if (fieldType.equals("int")) {
					if (StringUtils.isInteger(valueStr)) {
						value = StringUtils.intValue(valueStr);
					} else {
						value = 0;
					}
				} else if (fieldType.equals("BigDecimal")) {
					if (!StringUtils.isEmpty(valueStr)) {
						try {
							value = new BigDecimal(valueStr);
						} catch (Exception e) {
							throw new ExceptionHandler(e);
						}
					}
				} else if (fieldType.equals("boolean")) {
					value = Boolean.valueOf(valueStr);
				} else {
					// 默认当作 String 类型处理
					value = valueStr;
				}
				// TODO:如果有其它数据类型，在这里添加

				try {
					setMethod = getSetterMethod(cls, field);
					if (setMethod != null) {
						setMethod.invoke(obj, value);
					}
				} catch (SecurityException e) {
					throw new ExceptionHandler(e);
				} catch (IllegalArgumentException e) {
					throw new ExceptionHandler(e);
				} catch (IllegalAccessException e) {
					throw new ExceptionHandler(e);
				} catch (InvocationTargetException e) {
					throw new ExceptionHandler(e);
				}

			}
		}
		fieldName = null;
		fieldType = null;
		valueStr = null;
		value = null;
		setMethod = null;
		fields = null;
		cls = null;
	}

}
